#include "SysState.h"
#include "HarmonySearch.h"

using namespace std;



//const goes here
//default wait time at the beginning
const double default_wait_time = 20;
const int file_total = 30;
const int join_job_total = 1 * file_total * (file_total - 1) / 2;
const int exec_round = 10;
const int min_done_job = 20000;
const double default_lambda = 0.1;
//which algorithm to use: 1 for hs, 2 for sjf
const int algorithm_type = 1;


// HarmonySearch related parameters.
const int HMS = file_total;
const double HMCR = 0.9;
const double PAR = 0.3;
// ALPHA does not affect HS at all.
const double ALPHA = 1;
const double BETA = 0.5;

int getPoisson(double lambda);

void generator(SysState& sys_state, double exe_time);

void exec(SysState& sys_state, double exe_time, const vector<bool>& decision);

void analysis(SysState& sys_state);

double sjf(vector<bool>& soln, const SysState& sys_state);


int main(){
	srand(time(NULL));
	cout.unsetf(ios::floatfield); 
	cout.precision(3);

	if(algorithm_type == 1){
		cout << "test hs algorithm" << endl;
	}else{
		cout << "test sjf algorithm" << endl;
	}


	SysState state(file_total, join_job_total, default_lambda, exec_round);
	HarmonySearch hs(HMS, HMCR, PAR, ALPHA, BETA, file_total, state);

	for(int i =0; i<exec_round; i++){
		double exe_time = 0;

		vector<bool> decision;


		while(state.total_done_job < min_done_job ){
			//generate the tasks and push into state
			generator(state, exe_time);



			if(algorithm_type == 1){	
				decision = hs.runHarmonySearch();
				// Gets execution time for this execution batch.
				exe_time = getExecTime(state, decision);
			}else if(algorithm_type == 2){
				exe_time = sjf(decision, state);
			}else{
				cerr<< "incorrect algorithm type" << endl;
			}
			//perform simulate execution
			exec(state, exe_time, decision);
		}
		state.pwt_queue[i] = state.cum_time/state.total_done_job;
		state.maxpwt_queue[i] = state.max_time;
		cout << state.cum_time/state.total_done_job << " "<< state.max_time << endl;
		state.reset(file_total, join_job_total);
		sleep(1);
	}
	analysis(state);
	return 0;
}


//Poisson number generator program
int getPoisson(double lambda){
	double l = exp(-lambda);
	double p = 1.0;
	int k = 0;
	do{
		k++;
		p *= ((double)random())/RAND_MAX;
	}while(p>l);
	return k-1;
}

void generator(SysState& state, double exe_time){
	//if exe_time is 0, either just start or something went wrong..
	//if exe_time is less than 0.. we are doomed!
	//in this case, wait for sometime
	if(exe_time <= 0){
		exe_time = default_wait_time;
	}

	//put things into queue and update some values
	for(int i = 0; i<state.n; ++i){
		for(int j = i; j<state.n; ++j){
			if(state.L[i][j] != 0){
				//generate number of jobs
				int num = getPoisson(state.L[i][j]*exe_time);
				

				if(state.B[i][j] != 0){
					//add cumulative wait time for old jobs
					state.q_cum[i][j] += state.B[i][j] * exe_time;
					//update max wait time if any old job exist
					state.q_max[i][j] += exe_time;
				}

				if (num != 0){
					state.q_cum[i][j] += 0.5 * exe_time * num;
					if(state.B[i][j] == 0){
						state.q_max[i][j] = exe_time* (0.6 + 0.4*rand()/RAND_MAX);
					}
					state.B[i][j] += num;
				}	
			}	
		}	
	}	

}

void exec(SysState& state, double exe_time, const vector<bool>& decision){
	if(exe_time <= 0){
		cout << "we are doomed " << exe_time << endl;
		return;
	}
	
	int sum =0;
	for(int i=0; i<state.n; ++i){
		sum += decision[i];
	}
	if(sum == 0){
		cout << "we are also doomed" << endl;
	}
	 //cout << sum << endl;

	for(int i=0; i<state.n; ++i){
		for(int j=i; j<state.n; ++j){
			if(decision[i] && decision[j] && state.B[i][j] != 0){
				// calculate time, etc
				if(i == j){
					exe_time = exe_time - state.Ts[i] - state.Tn[i][i];
				}else{
					exe_time = exe_time - state.Ts[i] - state.Ts[j] - state.Tn[i][j];
				}
				state.q_cum[i][j] += exe_time * state.B[i][j];
				state.q_max[i][j] += exe_time;
				state.q_count[i][j] += state.B[i][j];
				state.total_done_job += state.B[i][j];
				state.cum_time += state.q_cum[i][j];
				if(state.q_max[i][j] > state.max_time){
					state.max_time = state.q_max[i][j];
				}

				//perform the reset operations
				state.q_cum[i][j] = 0;
				state.B[i][j] = 0;
				state.q_max[i][j] = 0;
				//cout << state.total_done_job << endl;
			}
		}
	}

}

void analysis(SysState& state){
	/*
	cout << "average wait time is " << state.cum_time/state.total_done_job << endl;
	cout << "max wait time is " << state.max_time << endl;
	cout << "total work done " << state.total_done_job << endl << endl;
	*/
	double cum_sum = 0;	
	double max_sum = 0;
	for(int i=0; i<state.exe_round; i++){
		cum_sum += state.pwt_queue[i];
		max_sum += state.maxpwt_queue[i];
	}

	cout << "AVG avg_pwt is " << cum_sum/state.exe_round <<endl;
	cout <<	"AVG max_pwt is " << max_sum/state.exe_round << endl;

}

